package com.yeskery.nut.util;

import java.math.BigDecimal;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * JDBC 工具类
 * @author sprout
 * 2022-06-07 11:01
 */
public class JdbcUtils {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(JdbcUtils.class.getName());

    /** oracle.sql.TIMESTAMP */
    private static final String ORACLE_SQL_TIMESTAMP = "oracle.sql.TIMESTAMP";
    /** oracle.sql.TIMESTAMPTZ */
    private static final String ORACLE_SQL_TIMESTAMPTZ = "oracle.sql.TIMESTAMPTZ";
    /** oracle.sql.DATE */
    private static final String ORACLE_SQL_DATE = "oracle.sql.DATE";
    /** java.sql.Timestamp */
    private static final String JAVA_SQL_TIMESTAMP = "java.sql.Timestamp";

    /**
     * 私有化构造方法
     */
    private JdbcUtils() {
    }

    /**
     * 关闭连接对象
     * @param connection 关闭连接对象
     */
    public static void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException ex) {
                logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "closeConnection",
                        "Could not close JDBC Connection.", ex);
            } catch (Throwable ex) {
                logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "closeConnection",
                        "Unexpected exception on closing JDBC Connection.", ex);
            }
        }
    }

    /**
     * 关闭Statement对象
     * @param statement Statement对象
     */
    public static void closeStatement(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException ex) {
                logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "closeConnection",
                        "Could not close JDBC Statement.", ex);
            } catch (Throwable ex) {
                logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "closeConnection",
                        "Unexpected exception on closing JDBC Statement.", ex);
            }
        }
    }

    /**
     * 关闭结果集对象
     * @param resultSet 结果集对象
     */
    public static void closeResultSet(ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException ex) {
                logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "closeConnection",
                        "Could not close JDBC ResultSet.", ex);
            } catch (Throwable ex) {
                logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "closeConnection",
                        "Unexpected exception on closing JDBC ResultSet.", ex);
            }
        }
    }

    /**
     * 从结果集获取指定类型的结果
     * @param rs 结果集
     * @param index 结果索引
     * @param clazz 结果类型
     * @return 结果
     * @throws SQLException SQLException
     */
    public static Object getResultSetValue(ResultSet rs, int index, Class<?> clazz) throws SQLException {
        if (clazz == null) {
            return getResultSetValue(rs, index);
        }

        Object value;
        if (String.class == clazz) {
            return rs.getString(index);
        } else if (boolean.class == clazz || Boolean.class == clazz) {
            value = rs.getBoolean(index);
        } else if (byte.class == clazz || Byte.class == clazz) {
            value = rs.getByte(index);
        } else if (short.class == clazz || Short.class == clazz) {
            value = rs.getShort(index);
        } else if (int.class == clazz || Integer.class == clazz) {
            value = rs.getInt(index);
        } else if (long.class == clazz || Long.class == clazz) {
            value = rs.getLong(index);
        } else if (float.class == clazz || Float.class == clazz) {
            value = rs.getFloat(index);
        } else if (double.class == clazz || Double.class == clazz ||
                Number.class == clazz) {
            value = rs.getDouble(index);
        } else if (BigDecimal.class == clazz) {
            return rs.getBigDecimal(index);
        } else if (java.sql.Date.class == clazz) {
            return rs.getDate(index);
        } else if (java.sql.Time.class == clazz) {
            return rs.getTime(index);
        } else if (java.sql.Timestamp.class == clazz || java.util.Date.class == clazz) {
            return rs.getTimestamp(index);
        } else if (byte[].class == clazz) {
            return rs.getBytes(index);
        } else if (Blob.class == clazz) {
            return rs.getBlob(index);
        } else if (Clob.class == clazz) {
            return rs.getClob(index);
        } else if (clazz.isEnum()) {
            Object obj = rs.getObject(index);
            if (obj instanceof String) {
                return obj;
            } else if (obj instanceof Number) {
                Number number  = (Number) obj;
                return number.intValue();
            } else {
                return rs.getString(index);
            }
        } else {
            return getOtherResultSetValue(rs, index, clazz);
        }

        return (rs.wasNull() ? null : value);
    }

    /**
     * 从结果集获取结果
     * @param rs 结果集
     * @param index 结果索引
     * @return 结果
     * @throws SQLException SQLException
     */
    public static Object getResultSetValue(ResultSet rs, int index) throws SQLException {
        Object obj = rs.getObject(index);
        String className = null;
        if (obj != null) {
            className = obj.getClass().getName();
        }
        if (obj instanceof Blob) {
            Blob blob = (Blob) obj;
            obj = blob.getBytes(1, (int) blob.length());
        } else if (obj instanceof Clob) {
            Clob clob = (Clob) obj;
            obj = clob.getSubString(1, (int) clob.length());
        } else if (ORACLE_SQL_TIMESTAMP.equals(className) || ORACLE_SQL_TIMESTAMPTZ.equals(className)) {
            obj = rs.getTimestamp(index);
        } else if (className != null && className.startsWith(ORACLE_SQL_DATE)) {
            String metaDataClassName = rs.getMetaData().getColumnClassName(index);
            if (JAVA_SQL_TIMESTAMP.equals(metaDataClassName) || ORACLE_SQL_TIMESTAMP.equals(metaDataClassName)) {
                obj = rs.getTimestamp(index);
            } else {
                obj = rs.getDate(index);
            }
        } else if (obj instanceof java.sql.Date) {
            if (JAVA_SQL_TIMESTAMP.equals(rs.getMetaData().getColumnClassName(index))) {
                obj = rs.getTimestamp(index);
            }
        }
        return obj;
    }

    /**
     * 从结果集获取其他类型
     * @param rs 结果集
     * @param index 结果索引
     * @param clazz 类型
     * @return 结果
     * @throws SQLException SQLException
     */
    private static Object getOtherResultSetValue(ResultSet rs, int index, Class<?> clazz) throws SQLException {
        try {
            return rs.getObject(index, clazz);
        } catch (AbstractMethodError err) {
            logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "getResultSetValue",
                    "JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method.", err);
        } catch (SQLFeatureNotSupportedException ex) {
            logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "getResultSetValue",
                    "JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method.", ex);
        } catch (SQLException ex) {
            logger.logp(Level.SEVERE, JdbcUtils.class.getName(), "getResultSetValue",
                    "JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method.", ex);
        }

        String typeName = clazz.getSimpleName();
        switch (typeName) {
            case "LocalDate":
                return rs.getDate(index);
            case "LocalTime":
                return rs.getTime(index);
            case "LocalDateTime":
                return rs.getTimestamp(index);
            default:
        }

        return getResultSetValue(rs, index);
    }
}
